WR.util.namespace('WR.ui.PhotoWall');

/**
 * Photo wall item.
 * Abstract class.
 *
 * @param config - look to the WR.ui.PhotoWall.Category and WR.ui.PhotoWall.Photo documentation
 */
WR.ui.PhotoWall.Item = function(config) {
	$.extend(this, config);
    this.zoomData = {};
    this.zoom = 1;
};

WR.ui.PhotoWall.Item.prototype = {
    // All next properties are read-only! (may be set only from the constructor)
    x: 0, y: 0, z: 0,
    title: '',
    id: null,
    wall: null,
    el: null,
    rendered: false,

    template: null, // will be overriden
    type: 'item', // will be overriden

    // @private
    zoomData: {},
    zoom: 1,

    /**
     * Render item on the photo-wall
     */
    render: function() {
        if (this.wall && !this.rendered) {
            this.el = $(this.template.apply(this)).appendTo(this.wall.el);

            // Override .render() and use .zoomData to store original values

            // Linking data
            this.el.data('object', this);

            this.rendered = true;

            // Bad parody on event dispatching :)
            this.afterRender();
        }
    },

    /**
     * Bad parody on event dispatching :)
     */
    afterRender: $.noop,

    /**
     * Set some item  properties.
     * Marks item as changed (as well as the wall)
     *
     * @param {Object} params - new params. E.g.: { x: 123, y: 67, title: 'New title' }
     */
    change: function(params) {
        if (this.wall) {
            if ($.isArray(this.wall.undo)) {
                var undo = {};
                $.each(params, function(k) { undo[k] = item[k]; });
                undo = { type: this.type, id: this.id, from: undo, to: params };
                this.wall.undo.push(undo);
            }

            // Indicating that some changes has occurred!
            this.wall.changed = true;
        }

        $.extend(this, params);

        // Creating additional hash-map
        this.changes = $.extend(this.changes, params);
    },
	
	updateUI: function(changes) {
		var p = params = {
			x: changes.x,
			y: changes.y,
			z: changes.z
		};
		if (this.el) {
			this.el.css({
				left: 'x' in p && p.x || undefined,
				top: 'y' in p && p.y || undefined,
				zIndex: 'z' in p && p.z || undefined
			});
		}
		this.change(p);
	},

    /**
     * Set item zoom
     * Normal zoom is 1 
     * This is abstract method.
     *
     * @param {Number} newZoom - float zoom value
     */
    setZoom: function(newZoom) {
        if (isNaN(newZoom))
            return;
        // To be overriden
    }
};


//======================================================= Category =====================================================
/**
 * Photo category
 *
 * @param {Object} config - The config object
 * @constructor
 *
 * @config {Number} x - CSS left. Default: 0
 * @config {Number} y - CSS top. Default: 0
 * @config {Number} z - CSS z-index. Default: 0
 * @config {String} title - String title (actually, it may be HTML). Default: ''
 * @config {Number/String} id - id of a category. Required!
 * @config {WR.ui.PhotoWall} wall - parent wall. Required!
 * @config {WR.util.Template} template - HTML template for rendering. Default: ... (look into code)
 * @config {Boolean} cache - enable photo elements cache.
 *                          If enabled category's photo set will be cached for all further actions. Default: false
 */
WR.ui.PhotoWall.Category = function(config) {
    if (typeof config == "string") 
		config = { id: new Date().getTime(), title: config };
	WR.ui.PhotoWall.Category.superconstructor.call(this, config);
};

WR.ui.PhotoWall.Category.prototype = $.extend(new WR.ui.PhotoWall.Item(), {
    /**
     * @link WR.ui.PhotoWall.Item - look for basic params
     */
    // Type is needed for undo mechanism
    type: 'category',

    // All next properties are read-only! (may be set only from the constructor)
    template: new WR.util.Template(
        '<div class="wr-photo-category wr-unselectable" unselectable="on" ' +
            'style="left:{{ x }}px;top:{{ y }}px;z-index:{{ z }};">' +
            '<a class="wr-direct-link" name="photo-cat-{{ id }}"></a>' +
            '{{ title }}' +
        '</div>'
    ),

    cache: false,

    /**
     * Render photo on the photo-wall
     * @override
     */
    render: function() {
        if (this.rendered)
            return;

        // Call super-method
        this.constructor.superprototype.render.call(this);

        // Configure .zoomData for zooming
        var el = this.el, zd = this.zoomData;
        // Storing initial text paddings
        $.each(
            'Top Right Bottom Left'.split(' '),
            function() {
                var n = 'padding' + this;
                zd[n] = el.css(n);
            }
        );
        // Storing initial font-size
        zd.fontSize = el.css('fontSize');
    },

    // @override
    setZoom: function(newZoom) {
        if (isNaN(newZoom))
            return;

        var elStyles = {
            left: this.x * newZoom,
            top: this.y * newZoom
        };

        if (newZoom === 1) // Reset zoom changes
            $.extend(elStyles, this.zoomData);
        else
            $.each(this.zoomData, function(k, v) { elStyles[k] = Math.round(parseInt(v) * newZoom); });

        this.el.css(elStyles);

        this.zoom = newZoom;
    }
});

WR.ui.PhotoWall.Category.superconstructor = WR.ui.PhotoWall.Item;
WR.ui.PhotoWall.Category.superprototype = WR.ui.PhotoWall.Item.prototype;
WR.ui.PhotoWall.Category.prototype.constructor = WR.ui.PhotoWall.Category;

//========================================================= Photo ======================================================
/**
 * Photo item
 *
 * @param {Object} config - The config object
 * @constructor
 *
 * @config {Number} x - CSS left. Default: 0
 * @config {Number} y - CSS top. Default: 0
 * @config {Number} z - CSS z-index. Default: 0
 * @config {Number} width - CSS width of an image (excluding paddings). Default: 0
 * @config {String} title - String title (actually, it may be HTML). Default: ''
 * @config {String} url - photo URL. Required!
 * @config {Number/String} id - id of a photo. Required!
 * @config {Number/String} categoryId - id of a category for this photo. Required!
 * @config {WR.ui.PhotoWall} wall - parent wall. Required!
 * @config {WR.util.Template} template - HTML template for rendering. Default: ... (look into code)
 */
WR.ui.PhotoWall.Photo = function(config) {
	if (typeof config == "string") 
		config = { id: new Date().getTime(), url: config };
    WR.ui.PhotoWall.Photo.superconstructor.call(this, config);
};

WR.ui.PhotoWall.Photo.prototype = $.extend(new WR.ui.PhotoWall.Item(), {
    /**
     * @link WR.ui.PhotoWall.Item - look for basic params
     */
    // Type is needed for undo mechanism
    type: 'photo',

    // All properties are read-only! (may be set only from the constructor)
    width: 0,
    url: '',
    categoryId: null,

    template: new WR.util.Template(
        '<div class="wr-photo-item wr-unselectable" unselectable="on" ' +
                'style="left:{{ x }}px;top:{{ y }}px;z-index:{{ z }};width:{{ width }}px;">' +
            '<a class="wr-direct-link" name="photo-{{ id }}"></a>' +
            '<img src="{{ url }}" alt="{{ title }}"/>' +
            '<div class="wr-photo-description wr-unselectable" unselectable="on">{{ title }}</div>' +
        '</div>'
    ),

    /**
     * Render photo on the photo-wall
     * @override
     */
    render: function() {
        if (this.rendered)
            return;

        // Call super-method
        this.constructor.superprototype.render.call(this);

        // Lazy load images
        if ($.fn.lazyload) {
            this.el.find('img').lazyload({
                placeholder: this.wall.cssPath + 'images/loading-photo.gif',
                effect: "fadeIn",
                container: this.wall.el
            });
		}

        // Handle empty title
        var descrEl = this.el.find('.wr-photo-description');
        if (!this.title)
            descrEl.hide();

        // Backup original box sizes for zooming 
        if (this.width === 0) {
            var self = this;
            this.el.find('img').bind("load", function() {
                self.el.width(self.width = $(this).width());
                self.backupBox();
            });
        } else {
            this.backupBox();
        }
    },

    backupBox: function() {
        var el = this.el, descrEl = el.find('.wr-photo-description');

        // Configure .zoomData for zooming
        var zd = this.zoomData;
        zd.width = this.el.width();
        zd.descrEl = {};
        // Storing initial text paddings
        $.each(
            'Top Right Bottom Left'.split(' '),
            function() {
                var n = 'padding' + this;
                zd[n] = el.css(n);
                zd.descrEl[n] = descrEl.css(n);
            }
        );
        // Storing initial font-size
        zd.descrEl.fontSize = descrEl.css('fontSize');
    },

    // @override
    setZoom: function(newZoom) {
        if (isNaN(newZoom))
            return;

        var el = this.el, descrEl = el.find('.wr-photo-description');

        var elStyles = {
            left: this.x * newZoom,
            top: this.y * newZoom
        };

        var descrElStyles = {};

        if (newZoom === 1) {
            $.extend(elStyles, this.zoomData);
            $.extend(descrElStyles, this.zoomData.descrEl);
            el.find('img').width('');
        } else {
            $.each(this.zoomData, function(k, v) { elStyles[k] = Math.round(parseInt(v) * newZoom); });
            $.each(this.zoomData.descrEl, function(k, v) { descrElStyles[k] = Math.round(parseInt(v) * newZoom); });
            el.find('img').width('100%');
        }

        el.css(elStyles);
        descrEl.css(descrElStyles);

        this.zoom = newZoom;
    }
});

WR.ui.PhotoWall.Photo.superconstructor = WR.ui.PhotoWall.Item;
WR.ui.PhotoWall.Photo.superprototype = WR.ui.PhotoWall.Item.prototype;
WR.ui.PhotoWall.Photo.prototype.constructor = WR.ui.PhotoWall.Photo;
